package deviceController

import (
	"errors"
	"fmt"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntityManager"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEnum/enumDataCommStatus"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEnum/enumOperateMode"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedisRpc"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedisService"
	"gitee.com/fierce_wolf/go-fox-edge-common/commRedisStatus"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/GID"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/String"
	"go-fox-edge-device/deviceLogger"
	"go-fox-edge-device/deviceScript"
	"net/http"
	"time"
)

type deviceExecuteController struct {
}

// Scheduler 创建一个线程
func (e *deviceExecuteController) Scheduler() {
	go e.scheduler()
}

// 线程函数
func (e *deviceExecuteController) scheduler() {
	// 创建一个线程ID
	routineKey := "Execute:" + String.ToString(GID.Alloc())

	// 为当前线程，进行脚本引擎的初始化
	deviceScript.EngineInitialize.Initialize(routineKey)

	for true {
		time.Sleep(1 * time.Millisecond)

		e.execute(routineKey)
	}
}

func (e *deviceExecuteController) execute(routineKey string) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			deviceLogger.PrintLogger(fmt.Sprintf("deviceExecuteController.execute() 捕获到异常:%v", r))
		}
	}()

	// 检查：是否装载完毕
	if !commEntityManager.EntityManager.Initialized {
		time.Sleep(1 * time.Second)
		return
	}

	taskRequestVO, err := commRedisRpc.Device.Server.Request.Pop(1 * time.Second)
	if err != nil {
		return
	}

	taskRespondVO := commRedisRpc.Type.TaskRespondVO.New()
	taskRespondVO.Uuid = taskRequestVO.Uuid
	taskRespondVO.ClientName = taskRequestVO.ClientName
	taskRespondVO.Timeout = taskRequestVO.Timeout
	taskRespondVO.Code = http.StatusOK

	// 逐个步骤的操作设备
	for _, operateRequestVO := range taskRequestVO.RequestVOS {
		operateRespondVO, err := e.executeOperateRequest(routineKey, operateRequestVO)
		if err != nil {
			operateRespondVO = e.makeExceptionRespondVO(operateRequestVO, err.Error())
		}

		taskRespondVO.RespondVOS = append(taskRespondVO.RespondVOS, operateRespondVO)
	}

	// 返回数据设备服务
	err = commRedisRpc.Device.Server.Respond.Push(taskRespondVO.Uuid, taskRespondVO)
	if err != nil {
		return
	}

	// 记录用户操作：发送给持久化服务
	e.recordOperate(taskRespondVO)
}

func (e *deviceExecuteController) executeOperateRequest(routineKey string, requestVO *commRedisRpc.OperateRequestVO) (*commRedisRpc.OperateRespondVO, error) {
	deviceName := requestVO.DeviceName
	timeout := requestVO.Timeout

	// 获得设备详细信息
	deviceEntity := commRedisService.GetDeviceEntity(deviceName)
	if deviceEntity == nil {
		return nil, errors.New("指定名称的设备不存在：" + deviceName)
	}

	// 获得操作实体
	operateEntity := commRedisService.GetOperateEntity(deviceEntity.Manufacturer, deviceEntity.DeviceType, requestVO.OperateName)
	if operateEntity == nil {
		return nil, errors.New("指定的操作，不存在：" + deviceName)
	}

	if operateEntity.EngineType != "JavaScript" {
		return nil, errors.New("只支持JavaScript类型的解码器")
	}

	// 通过时间戳，判断通道服务是否正在运行
	isActive := commRedisStatus.IsActive(commRedisStatus.ModelTypeChannel, deviceEntity.ChannelType, 60*1000)
	if !isActive {
		return nil, errors.New("指定的Channel服务尚未运行：" + deviceEntity.ChannelType)
	}

	// 组合参数：先组合设备上的参数，再组合操作上的参数，也就是操作参数的优先级高于设备上的参数
	param := make(map[string]interface{})
	Map.Append(param, deviceEntity.DeviceParam)
	if !Method.HasEmpty(requestVO.Param) {
		Map.Append(param, requestVO.Param)
	}

	// 交换类操作
	if enumOperateMode.Exchange == requestVO.OperateMode {
		value, err := deviceScript.EngineExecutor.Exchange(routineKey, deviceName, deviceEntity.Manufacturer, deviceEntity.DeviceType, operateEntity, param, timeout)
		if err != nil {
			return nil, err
		}

		status := make(map[string]interface{})
		status[enumDataCommStatus.CommSuccessTime] = time.Now().UnixMilli()
		status[enumDataCommStatus.CommFailedTime] = 0
		status[enumDataCommStatus.CommFailedCount] = 0

		operateRespondVO := &commRedisRpc.OperateRespondVO{}
		operateRespondVO.BindRequestVO(requestVO)
		operateRespondVO.Data = make(map[string]interface{})
		operateRespondVO.Data[enumDataCommStatus.CommStatus] = status
		operateRespondVO.Data[enumDataCommStatus.CommValue] = value
		operateRespondVO.Code = http.StatusOK
		return operateRespondVO, nil
	}

	// 交换类操作
	if enumOperateMode.Publish == requestVO.OperateMode {
		err := deviceScript.EngineExecutor.PublishPublish(routineKey, deviceName, deviceEntity.Manufacturer, deviceEntity.DeviceType, operateEntity, param, timeout)
		if err != nil {
			return nil, err
		}

		operateRespondVO := &commRedisRpc.OperateRespondVO{}
		operateRespondVO.BindRequestVO(requestVO)
		operateRespondVO.Code = http.StatusOK
		return operateRespondVO, nil
	}

	return nil, errors.New("不支持的操作类型")
}

func (e *deviceExecuteController) makeExceptionRespondVO(requestVO *commRedisRpc.OperateRequestVO, errorMessage string) *commRedisRpc.OperateRespondVO {
	status := make(map[string]interface{})
	status[enumDataCommStatus.CommSuccessTime] = 0
	status[enumDataCommStatus.CommFailedTime] = time.Now().UnixMilli()
	status[enumDataCommStatus.CommFailedCount] = 1

	operateRespondVO := &commRedisRpc.OperateRespondVO{}
	operateRespondVO.BindRequestVO(requestVO)
	operateRespondVO.Code = http.StatusInternalServerError
	operateRespondVO.Msg = errorMessage

	return operateRespondVO
}

func (e *deviceExecuteController) recordOperate(taskRespondVO *commRedisRpc.TaskRespondVO) {
	// 定义一个最终的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
		}
	}()

	var respondVOS []*commRedisRpc.OperateRespondVO
	for _, operateRespondVO := range taskRespondVO.RespondVOS {
		// 场景1：用户手动标识的要求记录的数据
		if operateRespondVO.Record == true {
			resultVO := &commRedisRpc.OperateRespondVO{}
			resultVO.BindRespondVO(operateRespondVO)
			resultVO.Record = true

			respondVOS = append(respondVOS, resultVO)
			continue
		}

		// 场景2：解码器要求强制记录的数据
		values := operateRespondVO.Data["value"]
		if values == nil {
			continue
		}
		vs := values.(map[string]interface{})
		_, ok := vs[deviceScript.EnumResult]
		if !ok {
			continue
		}

		resultVO := &commRedisRpc.OperateRespondVO{}
		resultVO.BindRespondVO(operateRespondVO)
		resultVO.Record = true

		respondVOS = append(respondVOS, resultVO)
		continue
	}

	// 检查：是否有真正需要记录的操作
	if len(respondVOS) == 0 {
		return
	}

	// 把数据内容，填写为新的操作步骤
	taskRespondVO.RespondVOS = respondVOS

	// 推送数据到持久化服务
	err := commRedisRpc.Persist.Client.RecordRequest.Push(taskRespondVO)
	if err != nil {
		return
	}
}
